// Library for Yadis; see http://yadis.org/
// does not execute HEAD step; any HTTP HEAD code is for examples
// in Appalachian; also goes through cache for GETs for present
// page

var YADIS_HTTP_HEADER = "X-XRDS-Location";
var YADIS_MIME_TYPE   = "application/xrds+xml";
var XML_TEXT_MIME_TYPE  = "text/xml";
var XML_APP_MIME_TYPE   = "application/xml";
var HTTP_CONTENT_TYPE = "Content-Type";

function Yadis(yadisId) {
	this._id = yadisId;
};

Yadis.prototype.discover = function(doc) {
	var httplib = new YadisHTTP(this._id);
	var location = httplib.getLocation();
	if (location != null && location != "") {
		return true;
	} else {
		var ms = doc.getElementsByTagName('meta');
		for (var i = 0; i < ms.length; i++) {
			if (ms[i].hasAttribute("http-equiv") && ms[i].getAttribute("http-equiv").toLowerCase().indexOf(YADIS_HTTP_HEADER.toLowerCase()) != -1) {
				return true;
			}
		}
	}
	return false;
};

Yadis.prototype.getAuthorities = function(doc) {
	AppalachianDebug.trace('yadis-lib.js','> Yadis.getAuthorities');
	var httplib = new YadisHTTP(this._id);
	var location = httplib.getLocation();
	if (location != null && location != "") {
		return this.processLocation(location);
	} else {
		return this.processBody(doc);
	}
	AppalachianDebug.trace('yadis-lib.js','< Yadis.getAuthorities');
};

Yadis.prototype.processBody = function(doc) {
	AppalachianDebug.trace('yadis-lib.js','> Yadis.processBody');
	var loc;
	var metas = doc.wrappedJSObject.getElementsByTagName('meta');
	for (var i = 0; i < metas.length; i++) {
		if (metas[i].hasAttribute('http-equiv')) {
			if (metas[i].getAttribute('http-equiv').toLowerCase() == YADIS_HTTP_HEADER.toLowerCase()) {
				loc = metas[i].getAttribute('content');
				break;
			}
		}
	}
	AppalachianDebug.trace('yadis-lib.js','< Yadis.processBody');
	return this.processLocation(loc);
};

Yadis.prototype.processLocation = function(url) {
	AppalachianDebug.trace('yadis-lib.js','> Yadis.processLocation ' + url);
	var httplib = new YadisHTTP(url);
	var resp = httplib.getBody(false, true);
	if (resp.isXRDS) {
		return this.evaluateBody(resp.body);
	} else {
		return [];
	}
	AppalachianDebug.trace('yadis-lib.js','< Yadis.processLocation');
};

Yadis.prototype.evaluateBody = function(xml) {
	AppalachianDebug.trace('yadis-lib.js','> Yadis.evaluateBody');
	var parser = new DOMParser();
	var doc = parser.parseFromString(xml, "text/xml");
	var nsResolver = function(prefix) {
		var p = null;
		if (prefix == 'xrds')
			p = 'xri://$xrds';
		else if (prefix == 'x')
			p = 'xri://$xrd*($v*2.0)';
		return p;
	};
	var iterator = doc.evaluate("//x:Service[x:Type='http://openid.net/signon/1.0' or x:Type='http://openid.net/signon/1.1']/x:URI", doc, nsResolver, XPathResult.ANY_TYPE,null);
	var elmt = iterator.iterateNext();
	var i = 0;
	var authorities = [];
	while (elmt) {
		authorities[i++] = elmt.textContent;
		elmt = iterator.iterateNext();
	}
	AppalachianDebug.trace('yadis-lib.js','< Yadis.evaluateBody');
	return authorities;
};

function YadisHTTP(url) {
	this._url = url;
	this._channel = null;
};

YadisHTTP.prototype.getLocation = function() {
	AppalachianDebug.trace('yadis-lib.js','> YadisHTTP.getLocation');
	this.observer.register();
	this.doGET(true, false); // look in cache, don't set Accept
	this.observer.unregister();
	this._channel = null;
	AppalachianDebug.trace('yadis-lib.js','< YadisHTTP.getLocation ' + this.observer._location);
	return this.observer._location;
};

YadisHTTP.prototype.getBody = function(from_cache, use_accept) {
	this.observer.register();
	var b = this.doGET(from_cache, use_accept);
	this.observer.unregister();
	this._channel = null;
	return ({
		body : b,
		isXRDS : this.observer._isXRDS,
		location : this.observer._location
	});
};

YadisHTTP.prototype.doHEAD = function() {
	var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
	var scriptableStream = Components.classes["@mozilla.org/scriptableinputstream;1"].getService(Components.interfaces.nsIScriptableInputStream);
	AppalachianDebug.trace('yadis-lib.js', 'doHEAD: ' + this._url);
	this._channel = ioService.newChannel(this._url,null,null);
	this._channel.QueryInterface(Components.interfaces.nsIRequest);
	this._channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
	this._channel.QueryInterface(Components.interfaces.nsIChannel);
	this.observer.setChannel(this._channel);
	var input = this._channel.open();
	scriptableStream.init(input);
	scriptableStream.close();
	input.close();
};

YadisHTTP.prototype.doGET = function(from_cache, use_accept) {
	if (from_cache == null) from_cache = false;
	var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
	var scriptableStream = Components.classes["@mozilla.org/scriptableinputstream;1"].getService(Components.interfaces.nsIScriptableInputStream);
	AppalachianDebug.trace('yadis-lib.js', 'doGET: ' + this._url);
	this._channel = ioService.newChannel(this._url,null,null);
	this._channel.QueryInterface(Components.interfaces.nsIRequest);
	this._channel.loadFlags |= from_cache ? Components.interfaces.nsIRequest.LOAD_FROM_CACHE : Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
	this._channel.QueryInterface(Components.interfaces.nsIChannel);
	if (use_accept) this._channel.setRequestHeader("Accept", YADIS_MIME_TYPE + "; q=1,text/html; q=0.9", false);
	this.observer.setChannel(this._channel);
	var input = this._channel.open();
	scriptableStream.init(input);
	var str = "";
	var avail;
	while ((avail = input.available()) > 0)
		str += scriptableStream.read(avail);
	scriptableStream.close();
	input.close();
	return str;
};

YadisHTTP.prototype.observer = {
	_location : null,

	_isXRDS : null,

	_channel : null,

	observe : function(subject, topic, data) {
		AppalachianDebug.trace('yadis-lib.js','> observe ' + topic);
		if (this._channel != null && subject == this._channel) {
			var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
			if (topic == "http-on-examine-response") {
				try {
					this._location = httpChannel.getResponseHeader(YADIS_HTTP_HEADER);
				} catch(e) {
					this._location = null;
				}
				try {
					this._isXRDS = (httpChannel.getResponseHeader(HTTP_CONTENT_TYPE).toLowerCase().indexOf(YADIS_MIME_TYPE) >= 0 || httpChannel.getResponseHeader(HTTP_CONTENT_TYPE).toLowerCase().indexOf(XML_TEXT_MIME_TYPE) >= 0 || httpChannel.getResponseHeader(HTTP_CONTENT_TYPE).toLowerCase().indexOf(XML_APP_MIME_TYPE) >= 0);
				} catch(e) {
					this._isXRDS = false;
				}
			}
		}
		AppalachianDebug.trace('yadis-lib.js','< observe');
	},

	register : function() {
		this._location = null;
		this._isXRDS = false;
		var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
		observerService.addObserver(this, "http-on-examine-response", false);
	},

	setChannel : function(channel) {
		this._channel = channel;
	},

	unregister : function() {
		var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
		observerService.removeObserver(this, "http-on-examine-response");
		this._channel = null;
	},

	QueryInterface : function(aIID) {
		if (aIID.equals(Components.interfaces.nsISupports) ||
		    aIID.equals(Components.interfaces.nsIObserver))
			return this;
		throw Components.results.NS_NOINTERFACE;
	}
};
